#include <linux/init.h>         //包含宏定义得头文件
#include <linux/module.h>       //包含初始化加载模块得头文件
#include <linux/miscdevice.h>   //杂项设备头文件
#include <linux/fs.h>           //文件操作集
#include <linux/uaccess.h>      //copy_to_user函数的文件

#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/errno.h>
#include <linux/atomic.h>       //原子操作头文件
/*包含头文件*/

/*****************
*注册杂项设备驱动，并生成设备节点

*****************/
/*文件操作集的结构体*/

struct device_test{
    dev_t dev_num;
    int major;
    int minor;
    struct cdev cdev_test;
    struct class* class;
    struct device *device;
    char kbuf[32];
};

struct device_test dev1;
static spinlock_t spinlock;//定义一个自旋锁
static int flag=1;

static int cdev_test_open(struct inode*inode,struct file*file)
{
    spin_lock(spinlock);//加锁
    //临界区
    if(flag!=0){
        spin_unlock(&spinlock);
        return -EBUSY;
    }
    flag=0;
    spin_unlock(&spinlock);//解锁

    file->private_data=&dev1;
    printk("This is cdev_test_open\n");
    return 0;
}

static ssize_t cdev_test_read(struct file * file,char _user * ubuf,size_t size,loff_t*loff_t)
{
    struct device_test *test_dev=(struct device_test*) file->private_data;
    if(copy_to_user(buf,test_dev->kbuf,strlen(test_dev->kbuf))!=0)
    {
        printk("copy_to_user is error\n");
        return -1;
    }
    return 0;
}

static int cdev_test_write(struct file * file,const char _user * ubuf,size_t size,loff_t*loff_t){
    struct device_test *test_dev=(struct device_test*) file->private_data;
    if(copy_form_user(buf,test_dev->kbuf,buf,size))!=0)
    {
        printk("copy_to_user is error\n");
        return -1;
    }
    printk("test_dev->kbuf is:%s\n",test_dev->kbuf);
    return 0;
}

static int cdev_test_release(struct inode*inode,struct file * file){
  
    printk("cdev_test_release is end!\n");
    spin_lock(&spinlock);//加锁
    //临界区
    flag=1;
    spin_unlock(&spinlock);//解锁


    return 0;
}

struct file_operations cdev_test_ops={
    .owner = THIS_MODULE,
    .open  = cdev_test_open,
    .release = cdev_test_release,
    .read = cdev_test_read,
    .write = cdev_test_write
};


/*功能实现*/
static int chrdev_init(void)
{
    dev1.dev_t dev_num;
    int reg;
    if(major_num)//major的值是由应用层传递的
    {
        dev1.dev_num = MKDEV(major_num,minor_num);
        reg=register_chrdev_region(dev_num,DEVICE_NUMBER,DEVICE_SNAME);//只是分配设备号并没有注册字符设备下一章节会展示
        if(reg<0)
        {
            printk("chrdev register error!\n");
        }
        printk("chrdev register ok!\n");
    }
    else//动态分配主次设备号
    {
        reg=alloc_chrdev_region(&dev_num,DEVICE_MINOR_NUMBER,DEVICE_NUMBER,DEVICE_ANAME);//动态分配设备号函数
        if(reg<0)
        {
            printk("chrdev register error!\n");
        }
        printk("chrdev register ok!\n");

        //取出主次设备号并打印出来
        major_num=MAJOR(dev_num);
        minor_num=MINOR(dev_num);
        printk("chrdev major_num is:%n\n",major_num);
        printk("chrdev minor_num is:%n\n",minor_num);
    }
    return 0;
    //映射地址在init函数内这样加载上驱动就映射完成了
    // vir_gpio5_dr=ioermap();
}




static int cdev_init(void)
{
    int ret;
    ret = misc_register(&misc_dev);//注册杂项设备，自动生成设备节点
    if(ret<0)
    {
        printk("misc register is error\n");
        return -1;
    }
    printk("misc register is ok\n");
    return 0;
}
static void misc_exit(void)//退出函数没有返回值
{
    misc_deregister(&misc_dev);
    printk("byb byb !"); 
}
/*功能实现*/

module_init(misc_init);
module_exit(misc_exit); 
/*驱动的出口与入口函数都在此处*/
MODULE_LICENSE("GPL");
/*申明信息*/
	
/*注意在开发板中加载驱动的时候一定要保持板卡的内核和编译的内核是一致的否则会报错
*查看设备节点：ls /dev/hello
*/

